package com.kpmg.datalake.common.service.impl;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.kpmg.datalake.common.constants.Constants;
import com.kpmg.datalake.common.dynamicdatasource.DBTypeEnum;
import com.kpmg.datalake.common.dynamicdatasource.DataSourceStoreService;
import com.kpmg.datalake.common.enums.ProjectSourceEnum;
import com.kpmg.datalake.common.enums.ProjectSyncStatusEnum;
import com.kpmg.datalake.common.enums.TopicEnum;
import com.kpmg.datalake.common.enums.UseOrDelEnum;
import com.kpmg.datalake.common.service.IDataLakeDataService;
import com.kpmg.datalake.common.service.IDbPrmsnService;
import com.kpmg.datalake.common.service.IEntBscinfService;
import com.kpmg.datalake.common.service.IProjectService;
import com.kpmg.datalake.common.service.RbacFunctionService;
import com.kpmg.datalake.common.service.SqlLoaderService;
import com.kpmg.datalake.common.thirdparty.datalake.DataLakeInterface;
import com.kpmg.datalake.common.thirdparty.datalake.projectlist.dto.ProjectResponse;
import com.kpmg.datalake.common.thirdparty.datalake.topictask.dto.TopicResponse;
import com.kpmg.datalake.common.thirdparty.datalake.topictask.dto.TopicResponse.ResponseData;
import com.kpmg.datalake.common.utils.AssertUtil;
import com.kpmg.datalake.common.utils.EncrypUtil;
import com.kpmg.datalake.common.utils.LogUtils;
import com.kpmg.datalake.common.utils.SpringUtil;
import com.kpmg.datalake.db.dao.ProjectMapper;
import com.kpmg.datalake.db.model.DbPrmsn;
import com.kpmg.datalake.db.model.EntBscinf;
import com.kpmg.datalake.db.model.Project;
import com.kpmg.datalake.operatelog.enums.LogTypeEnum;
import com.kpmg.datalake.operatelog.model.OperateLog;
import com.kpmg.datalake.operatelog.service.IOperateLogService;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/**
 * <p>
 * 项目基础信息 服务实现类
 * </p>
 *
 * @author azhao6
 * @since 2018-08-17
 */
@Service
public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> implements
    IProjectService {

  private static final Logger logger = LoggerFactory.getLogger(ProjectServiceImpl.class);

  @Resource
  private DataLakeInterface dataLakeInterface;
  @Resource
  private IEntBscinfService entBscinfService;
  @Resource
  private IDbPrmsnService dbPrmsnService;
  @Resource
  private IDataLakeDataService dataLakeDataService;
  @Resource
  private SqlLoaderService sqlLoaderService;
  @Resource
  private IOperateLogService operateLogService;
  @Resource
  private RbacFunctionService rbacFunctionService;
  @Value("${spring.datasource.druid.driverClassName}")
  private String driverClassName;
  @Value("${spring.datasource.druid.initialSize}")
  private int initialSize;
  @Value("${spring.datasource.druid.minIdle}")
  private int minIdle;
  @Value("${spring.datasource.druid.maxActive}")
  private int maxActive;
  @Value("${spring.datasource.druid.maxWait}")
  private int maxWait;
  @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
  private int timeBetweenEvictionRunsMillis;
  @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
  private int minEvictableIdleTimeMillis;
  @Value("${spring.datasource.druid.validationQuery}")
  private String validationQuery;
  @Value("${spring.datasource.druid.testWhileIdle}")
  private boolean testWhileIdle;
  @Value("${spring.datasource.druid.testOnBorrow}")
  private boolean testOnBorrow;
  @Value("${spring.datasource.druid.testOnReturn}")
  private boolean testOnReturn;

  @Override
  public List<Project> loadUserProjects(String usrId) {
    AssertUtil.assertNotEmpty(usrId, "用户不能为空");
    return baseMapper.loadUserProjects(usrId);
  }

  @Override
  public Page<Project> getProjectList(int currentPage, int pageSize, String queryParam) {
    Page<Project> page = new Page<>();
    page.setSize(pageSize);
    page.setCurrent(currentPage);
    return page.setRecords(baseMapper.selectProjectList(page, queryParam));
  }

  /**
   * 通过项目ID 获取项目信息 实体信息和 db信息
   *
   * @param projectId 项目ID
   * @return 项目信息
   */
  @Override
  public Project selectProjectAndEntAndDbById(String projectId) {
    AssertUtil.assertNotEmpty(projectId, "项目ID不能为空");
    return baseMapper.selectProjectAndEntById(projectId);
  }

  /**
   * 通过项目Name 获取项目信息 实体信息和 db信息
   *
   * @param projectName 项目ID
   * @return 项目信息
   */
  @Override
  public Project selectProjectByName(String projectName) {
    AssertUtil.assertNotEmpty(projectName, "项目name不能为空");
    return baseMapper.selectProjectByProjectName(projectName);
  }

  /**
   * 获取未完成项目
   *
   * @param projectSyncStatus 项目状态
   * @return 项目列表新
   */
  @Override
  public List<Project> getSyncNotCompleteProjectList(ProjectSyncStatusEnum projectSyncStatus) {
    Project project = new Project();
    project.setDelInd(UseOrDelEnum.USE.getIntCode());
    project.setSyncStatus(projectSyncStatus.getCode());
    return super.selectList(new EntityWrapper<>(project));
  }

  /**
   * 与dataLake项目同步
   */
  @Override
  public void syncProject() {
    ProjectResponse dataLakeProjectList = dataLakeInterface.getDataLakeProjectList();
    if (dataLakeProjectList != null && dataLakeProjectList.getCode() == 0 && !CollectionUtils
        .isEmpty(dataLakeProjectList.getData())) {
      for (ProjectResponse.ResponseData responseData : dataLakeProjectList.getData()) {
        Project project = super.selectById(responseData.getProjectId());
        try {
          ProjectServiceImpl current = (ProjectServiceImpl) AopContext.currentProxy();
          if (project == null) {
            current.insertProject(responseData);
          } else {
            //串讲时要求不自动更新，预留相关更新接口
            current.updateProject(responseData);
          }
        } catch (Exception e) {
          logger.error(e.getMessage());
        }
      }
    }
  }

  /**
   * 同步项目sft信息
   *
   * @param projectId 项目id
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  public void syncProjectSftpInfo(String projectId) {
    TopicResponse topicInfo = dataLakeInterface.getTopicInfo(projectId);
    if (topicInfo != null && topicInfo.getCode() == 0 && topicInfo.getData() != null) {
      Project project = super.selectById(projectId);
      if (project != null) {
        ResponseData data = topicInfo.getData();
        if (data != null) {
          project.setSftpIp(data.getIp());
          project.setSftpPort(Integer.valueOf(data.getPort()));
          project.setSftpUser(data.getUsername());
          //密码加密
          project.setSftpPwd(EncrypUtil.encryptAes(data.getPassword(), Constants.ENCRY_KEY));
          project.setSftpPath(data.getUrl());
          project.setUdtDt(new Date());
          super.updateById(project);
        }
      }
    }
  }

  /**
   * 初始化项目信息
   *
   * @param projectId projectId
   */
  @Override
  public void initProjectDatabase(String projectId) {
    Project project = super.selectById(projectId);
    dbPrmsnService.initIndustryInfo(project, true, TopicEnum.INIT_DB);
    //初始化数据库配置信息
    DbPrmsn dbPrmsn = dbPrmsnService.initDbPrmsnByProject(project);
    //初始化数据库信息
    dbPrmsnService.initDbPrmsnDatabase(dbPrmsn);
    //刷新数据源信息到数据源中
    this.refreshDatasource(dbPrmsn);
    //初始化行业建表信息
    dbPrmsnService.initIndustryInfo(project, true, null);
    //记录数据配置信息到数据库中
    String prjName = project.getPrjName().length() > 32 ? project.getPrjName().substring(0, 32)
        : project.getPrjName();
    dbPrmsn.setCrtUsrId(StringUtils.isEmpty(project.getPrjName()) ? ""
        : (prjName));
    dbPrmsn.setDbAcc(EncrypUtil.encryptAes(dbPrmsn.getDbAcc(), Constants.ENCRY_KEY));
    dbPrmsn.setDbPwd(EncrypUtil.encryptAes(dbPrmsn.getDbPwd(), Constants.ENCRY_KEY));
    dbPrmsnService.insert(dbPrmsn);
    dbPrmsnService.initIndustryInfo(project, false, TopicEnum.INIT_DB);

  }

  /**
   * 单个项目 数据同步
   *
   * @param projectId 项目ID
   */
  @Override
  public void syncProjectData(String projectId) throws Exception {
    Project project = super.selectById(projectId);
    OperateLog operateLog = new OperateLog();
    try {
      operateLog.setLogType(LogTypeEnum.SYSTEM.getCode());
      operateLog.setLogModule("interface");
      operateLog.setLogDesc("数据同步");
      operateLog.setMethodName("syncProjectData");
      operateLog.setClassName(getClass().getName());
      operateLog.setRequestParam(projectId);
      operateLog.setBeginTime(new Date());
      operateLog.setRequestStatus(0);
      operateLogService.insert(operateLog);
      //处理同步状态
      project.setSyncStatus(ProjectSyncStatusEnum.SYNCING.getCode());
      project.setSyncTime(new Date());
      super.updateById(project);
      List<String> fileList = null;
      try {
        fileList = dataLakeDataService.fileDownLoad(project);
      } catch (Exception e) {
        logger.error(e.getMessage());
        operateLog.setRequestResult(e.getMessage());
      }
      Map<String, Map<String, String>> stringMapMap = null;
      if (!CollectionUtils.isEmpty(fileList)) {
        stringMapMap = sqlLoaderService.loaderData(projectId, fileList);
        if (!CollectionUtils.isEmpty(stringMapMap)) {
          operateLog.setRequestResult(JSON.toJSONString(stringMapMap));
        }
      }
      operateLog.setEndTime(new Date());
      operateLog
          .setCostTime(operateLog.getEndTime().getTime() - operateLog.getBeginTime().getTime());
      operateLogService.updateById(operateLog);
      //判断是否项目处理成功,如果又失败
      ProjectSyncStatusEnum projectSyncStatus = ProjectSyncStatusEnum.SYNC_DONE;
      if (!CollectionUtils.isEmpty(stringMapMap)) {
        for (Map<String, String> map : stringMapMap.values()) {
          //sqlldr 有 异常抛出或者产生bad文件都是同步失败
          if (!StringUtils.isEmpty(map.get(SqlLoaderServiceImpl.ERROR_OUTPUT)) ||
              !StringUtils.isEmpty(map.get(SqlLoaderServiceImpl.BAD_OUTPUT))) {
            projectSyncStatus = ProjectSyncStatusEnum.SYNC_FAILED;
            break;
          }
        }
      } else if (CollectionUtils.isEmpty(fileList)) {
        projectSyncStatus = ProjectSyncStatusEnum.UN_SYNC;
      }
      project.setSyncStatus(projectSyncStatus.getCode());
      super.updateById(project);
    } catch (Exception e) {
      long endTime = System.currentTimeMillis();
      operateLog.setEndTime(new Date(endTime));
      operateLog.setCostTime(endTime - operateLog.getBeginTime().getTime());
      operateLog.setRequestStatus(1);
      operateLog.setRequestResult(e.getMessage());
      operateLogService.updateById(operateLog);
      logger.error(e.getMessage(), e);
      project.setSyncStatus(ProjectSyncStatusEnum.SYNC_FAILED.getCode());
      super.updateById(project);
      throw e;
    }
  }


  /**
   * 刷新数据源信息
   *
   * @param dbPrmsn dbPrmsn
   */
  private void refreshDatasource(DbPrmsn dbPrmsn) {
    DruidDataSource targetDataSource = new DruidDataSource();
    targetDataSource.setDriverClassName(driverClassName);
    targetDataSource.setInitialSize(initialSize);
    targetDataSource.setMinIdle(minIdle);
    targetDataSource.setMaxActive(maxActive);
    targetDataSource.setMaxWait(maxWait);
    targetDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
    targetDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
    targetDataSource.setValidationQuery(validationQuery);
    targetDataSource.setTestWhileIdle(testWhileIdle);
    targetDataSource.setTestOnBorrow(testOnBorrow);
    targetDataSource.setTestOnReturn(testOnReturn);
    targetDataSource.setUsername(dbPrmsn.getDbAcc());
    targetDataSource.setPassword(dbPrmsn.getDbPwd());
    targetDataSource.setDriverClassName(DBTypeEnum.getDriverClass(dbPrmsn.getDbTpnm()));
    targetDataSource.setUrl(DBTypeEnum
        .getUrl(dbPrmsn.getDbTpnm(), dbPrmsn.getIpAdr(), dbPrmsn.getPort().toString(),
            dbPrmsn.getDbInstnnm()));
    try {
      DataSourceStoreService dataSourceStoreService = SpringUtil
          .getBean(DataSourceStoreService.class);
      //不管原因直接刷新缓存
      dataSourceStoreService.refreshTargetDataSource(dbPrmsn.getPrjId(), targetDataSource);
    } catch (Exception e) {
      LogUtils.error(e.getMessage());
    }


  }


  /**
   * 新增项目
   *
   * @param responseData responseData
   */
  @Transactional(rollbackFor = Exception.class)
  public void insertProject(ProjectResponse.ResponseData responseData) {
    //处理实体信息
    EntBscinf entBscinf = new EntBscinf();
    entBscinf.setEntId(UUID.randomUUID().toString());
    entBscinf.setDelInd(UseOrDelEnum.USE.getIntCode());
    entBscinf.setIdyId(responseData.getIndustryId());
    entBscinf.setEntNm(responseData.getEntityName());
    entBscinf.setEntShrtnmEn(responseData.getEntityName());
    entBscinf.setEntShrtnm(responseData.getEntityName());
    //处理项目信息
    Project project = new Project();
    project.setPrjId(responseData.getProjectId());
    project.setPrjManager(responseData.getDataManager());
    project.setPrjDataOwner(responseData.getDataOwner());
    project.setPrjDeleGator(responseData.getDeleGator());
    project.setDelInd(UseOrDelEnum.USE.getIntCode());
    project.setPrjAli(responseData.getProjectAlias());
    project.setItsCode(responseData.getIBScode());
    project.setEntId(entBscinf.getEntId());
    project.setCrtDt(responseData.getInsertDate());
    project.setPrjName(responseData.getProjectName());
    project.setSyncStatus(ProjectSyncStatusEnum.UN_INIT.getCode());
    project.setPrjSource(ProjectSourceEnum.DATALAKE.getCode());
    //同步用户权限信息
    project.setEntityBasic(entBscinf);
    rbacFunctionService.syncProjectUserPermissions(project);
    //同步实体信息
    entBscinfService.insert(entBscinf);
    //同步项目主要信息
    super.insert(project);
  }

  /**
   * 跟新项目
   *
   * @param responseData responseData
   */
  @Transactional(rollbackFor = Exception.class)
  public void updateProject(ProjectResponse.ResponseData responseData) {
    //nothing to do
  }
}
